﻿// PubServer.cpp: 实现文件
//

#include "stdafx.h"
#ifndef _FAN_FAN_
#include "PubServer.h"
#include "UserMessage.h"
#include "FontFactory.h"
#include "NowDisplay.h"
#include "MinTimeDisplay.h"
#include "DPIForm.h"
#include "PubInfoDelegate.h"
#include "LPCSTRArray.h"
#include "Utility.h"

// CPubServer 对话框
#define IDR_SHOW	11
#define IDR_EXIT	12
extern CLPCWSTRArray auctionAry;

IMPLEMENT_DYNAMIC(CPubServer, CDialogEx)

CPubServer::CPubServer(bool startintray, CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_PUBSERVER, pParent), pubInfoDelegate(this)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDI_PUBSERVER);
	m_timerActive = nullptr; startInTray = startintray; allowClose = false; closeOper = CloseOper::Minimize;
	m_Timer = m_adjTimer = nullptr; m_pNode = nullptr; m_clientNum = -1;
}

CPubServer::~CPubServer()
{
}

void CPubServer::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_MIN, m_Min);
	DDX_Control(pDX, IDC_MAX, m_Max);
	DDX_Control(pDX, IDC_CLTNUM, m_CltNum);
	DDX_Control(pDX, IDC_AUCTION, m_Auction);
}


BEGIN_MESSAGE_MAP(CPubServer, CDialogEx)
	ON_USER_TIMER()
	ON_WM_SYSCOMMAND()
	ON_WM_CLOSE()
	ON_WM_CTLCOLOR()
	ON_MESSAGE(WM_SHOW_MAIN, &CPubServer::OnShowMain)
	ON_MESSAGE(WM_FORCE_CLOSE, &CPubServer::OnForceClose)
	ON_MESSAGE(WM_REFRESH_CLTNUM, &CPubServer::OnRefreshCltNum)
	ON_MESSAGE(WM_REFRESH_NODES, &CPubServer::OnRefreshNodes)
	ON_MESSAGE(WM_CHECK_SEND, &CPubServer::OnCheckSend)
	ON_BN_CLICKED(IDC_EXIT, &CPubServer::OnBnClickedExit)
	ON_BN_CLICKED(IDC_HIDE, &CPubServer::OnBnClickedHide)
	ON_CBN_SELCHANGE(IDC_AUCTION, &CPubServer::OnSelchangeAuction)
END_MESSAGE_MAP()


// CPubServer 消息处理程序
BOOL CPubServer::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	inTray = false;
	SetWindowText(PubServer_TITLE);
	m_fFactory = new CFontFactory();
	m_fFactory->Create(_T("Arial"), 30, 0, FW_BOLD)->AttachCtrl(m_Min)->AttachCtrl(m_Max);
	m_NowDisplay = new CNowDisplay(); m_NowDisplay->Create(this);
	m_MinTimeDisplay = new CMinTimeDisplay(); m_MinTimeDisplay->Create(this); m_MinTimeDisplay->setTextColor(RGB(255, 255, 0));
	CDPIForm(this, 12, 12)
		.ctrl(IDC_ST3).SizeToContent().left(10).ctrl(*m_NowDisplay).left()
		.ctrl(m_Auction).top().after(IDC_ST3, 4).alignRight(*m_NowDisplay, -20, true).ctrl(IDC_ST3).centerY(m_Auction)
		.ctrl(*m_NowDisplay).below(m_Auction, 10)
		.ctrl(m_Min).left().below(*m_NowDisplay, 30).ctrl(IDC_ST0).SizeToContent().above(m_Min, -6).centerX(m_Min)
		.ctrl(m_Max).after(m_Min, 10).centerY(m_Min).ctrl(IDC_ST1).SizeToContent().above(m_Max, -6).centerX(m_Max)
		.ctrl(*m_MinTimeDisplay).after(m_Max, 10).centerY(m_Min).ctrl(IDC_ST2).SizeToContent().above(*m_MinTimeDisplay, -6).centerX(*m_MinTimeDisplay)
		.ctrl(IDC_EXIT).top().alignRight(*m_MinTimeDisplay).ctrl(IDC_HIDE).alignWidth(IDC_EXIT).below(IDC_EXIT, 5)
		.ctrl(m_CltNum).alignWidth(IDC_EXIT).alignBottom(*m_NowDisplay)
		.ctrl(IDC_ST5).SizeToContent().centerX(m_CltNum).above(m_CltNum, -4)
		.resizeWindow(m_MinTimeDisplay, m_MinTimeDisplay);
	CRect r; GetWindowRect(&r); RECT r0; SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&r0, 0);
	r.MoveToXY(r0.right - r.Width() - CDPIForm::DPIX(20), r0.bottom - r.Height() - CDPIForm::DPIY(20)); MoveWindow(r);
	if (startInTray) ShowWindow(SW_MINIMIZE);
	m_Timer = SET_USER_TIMER(TimerPubServer + 2, 90); m_useSysTime = false; DoSystime();
	SET_USER_ONCE_TIMER(m_timerActive, TimerPubServer + 1, 100);
	return TRUE;
}

BOOL CPubServer::DestroyWindow()
{
	if (pubInfoDelegate.IsRunning()) pubInfoDelegate.StopService();
	KILL_USER_TIMER(m_Timer); KILL_USER_TIMER(m_adjTimer);
	m_NowDisplay->DestroyWindow(); delete m_NowDisplay; m_NowDisplay = nullptr;
	m_MinTimeDisplay->DestroyWindow(); delete m_MinTimeDisplay; m_MinTimeDisplay = nullptr;
	BOOL bRet = CDialogEx::DestroyWindow();
	DESTROY_USER_TIMER();
	delete m_fFactory;
	return bRet;
}

HBRUSH CPubServer::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
	switch (pWnd->GetDlgCtrlID()) {
	case IDC_MIN:
		pDC->SetTextColor(RGB(0, 0, 255)); break;
	case IDC_MAX:
		pDC->SetTextColor(RGB(0, 0, 255)); break;
	}
	return hbr;
}

IMPLEMENT_TIMER_EVENT(CPubServer)
{
	cst_tickTime_Page t;
	switch (nIDEvent)
	{
	case TimerPubServer + 1:
		EVENT_CHECK_ONCEMULTI_TIMER(m_timerActive, nIDEvent);
		if (startInTray) toTray();
		{
			stDelegateStartParam param;
			param.panel = nullptr; param.processer = nullptr; param.maxNum = 1000;
			pubInfoDelegate.StartService(&param);
		}
		ShowData();
		return;
	case TimerPubServer + 2:		//显示
	{
		EVENT_CHECK_MULTI_TIMER(m_Timer, nIDEvent);
		TICK tk = TICK::Now(); m_Now.SetTick(tk);
		setCtrlShowTime(m_Now.toPlain(TICK::Now()));
	}	break;
	case TimerPubServer + 3:		//校准
		EVENT_CHECK_MULTI_TIMER(m_adjTimer, nIDEvent);
		if (m_useSysTime)
			t.getLocalTime();
		else if (!m_pNode || !m_pNode->GetPubInfoTime(t))
		{
			m_WrongAdjTimes++;
			if (m_WrongAdjTimes >= 40)
			{
				DoSystime();
			}
			return;
		}
		m_WrongAdjTimes = 0;
		if (GetTimeDiff(t, true) < 0)
			setCtrlShowTime(m_Now.toPlain(TICK::Now()));
		else
		{
			m_NonAdjTimes++;
			if (m_NonAdjTimes >= 200)
			{
				KILL_USER_TIMER(m_adjTimer);
				SetTimeClr(m_useSysTime, m_adjTimer ? true : false);
			}
		}
		break;
	}
	if (m_useSysTime)
	{
		if (m_pNode && m_pNode->GetPubInfoTime(t)) DoBidtime(t);
	}
	else
	{
		if (!m_pNode || !m_pNode->GetPubInfoTime(t)) DoSystime();
	}
	TICK tk = TICK::Now();
	if (tk - tmoutChkTick > 60017)
	{
		tmoutChkTick = tk; pubInfoDelegate.CheckTimeout();
	}
}

afx_msg void CPubServer::OnClose()
{
	switch (closeOper)
	{
	case Prompt:
		if (MessageBox(CString(L"请确认是否需要退出") + PubServer_TITLE, L"退出确认", MB_ICONQUESTION | MB_YESNOCANCEL) == IDYES) break;
		closeOper = CloseOper::Minimize; return;
	case Minimize:
		toTray(); return;
	}
	recoverTray(true);
	allowClose = true;
	CDialogEx::OnClose();
}

void CPubServer::OnOK()
{
	if (allowClose) CDialogEx::OnOK();
}

void CPubServer::OnCancel()
{
	if (allowClose) CDialogEx::OnCancel();
}

afx_msg LRESULT CPubServer::OnForceClose(WPARAM wParam, LPARAM lParam)
{
	closeOper = CloseOper::NoPrompt; PostMsg(GetSafeHwnd(), WM_CLOSE);
	return 0;
}

afx_msg void CPubServer::OnSelchangeAuction()
{
	CPubInfoNode *pNew = nullptr;
	int i = m_Auction.GetCurSel(); if (i >= 0) pNew = (CPubInfoNode *)m_Auction.GetItemData(i);
	if (pNew == m_pNode) return;
	m_pNode = pNew; cst_tickTime_Page t;
	if (!m_pNode || !m_pNode->GetPubInfoTime(t))
		DoSystime();
	else
	{
		m_useSysTime = true; DoBidtime(t);
	}
	ShowData();
}

afx_msg LRESULT CPubServer::OnRefreshCltNum(WPARAM wParam, LPARAM lParam)
{
	cs_refresh.Lock();
	m_clientNum = (intptr_t)wParam; ShowClientNum();
	cs_refresh.Unlock();
	return 0;
}

afx_msg LRESULT CPubServer::OnRefreshNodes(WPARAM wParam, LPARAM lParam)
{
	if (m_Auction.GetSafeHwnd())
	{
		const CPubInfoNode *pNode = (const CPubInfoNode *)wParam;
		if (!pNode)	//新增或删除拍卖会
		{
			if (pubInfoDelegate.NodeAry().SetCombo(m_Auction, m_pNode))
			{
				OnSelchangeAuction(); pubInfoDelegate.NodeBin().clear();	//调整完m_pNode后清空bin
			}
		}
		else if (pNode == m_pNode)	//刷新数据
			ShowData();
	}
	return 0;
}

afx_msg LRESULT CPubServer::OnCheckSend(WPARAM wParam, LPARAM lParam)
{
	if (wParam) pubInfoDelegate.NodeAry().CheckSend((CPubInfoNode *)wParam);
	return 0;
}

afx_msg void CPubServer::OnBnClickedExit()
{
	closeOper = CloseOper::Prompt; PostMsg(GetSafeHwnd(), WM_CLOSE);
}

afx_msg void CPubServer::OnBnClickedHide()
{
	toTray();
}

afx_msg LRESULT CPubServer::OnShowMain(WPARAM wParam, LPARAM lParam)
{
	if (wParam != IDI_PUBSERVER) return 1;
	switch (lParam)
	{
	case WM_LBUTTONUP://左键单击显示主界面
		recoverTray(false); break;
	case WM_RBUTTONUP://右击弹出菜单
	{
		LPPOINT lpoint = new tagPOINT;
		::GetCursorPos(lpoint);//得到鼠标位置
		CMenu menu;
		menu.CreatePopupMenu();
		menu.AppendMenu(MFT_STRING, IDR_SHOW, L"打开主窗口");
		menu.AppendMenu(MFT_STRING, IDR_EXIT, L"退出");
		SetForegroundWindow();
		intptr_t xx = TrackPopupMenu(menu, TPM_RETURNCMD, lpoint->x, lpoint->y, NULL, this->m_hWnd, NULL);//显示菜单并获取选项ID
		if (xx == IDR_SHOW)
			recoverTray(false);
		else if (xx == IDR_EXIT)
			OnBnClickedExit();
		HMENU hmenu = menu.Detach();
		menu.DestroyMenu();
		delete lpoint;
	}break;
	case WM_LBUTTONDBLCLK:
	{
		//双击不处理
	}break;
	}
	return 0;
}

void CPubServer::OnSysCommand(UINT nID, LPARAM lParam)
{
	if (nID == SC_MINIMIZE)
		toTray();
	else
		CDialogEx::OnSysCommand(nID, lParam);
}

void CPubServer::toTray()//最小化到托盘
{
	if (inTray) return;
	NOTIFYICONDATA nid;
	nid.cbSize = (size_t)sizeof(NOTIFYICONDATA);
	nid.hWnd = this->m_hWnd;
	nid.uID = IDI_PUBSERVER;
	nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	nid.uCallbackMessage = WM_SHOW_MAIN;
	nid.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_PUBSERVER));
	wcscpy_s(nid.szTip, PubServer_TITLE);
	Shell_NotifyIcon(NIM_ADD, &nid);//在托盘区添加图标  
	ShowWindow(SW_HIDE);//隐藏主窗口 
	inTray = true;
}

void CPubServer::recoverTray(bool isClose)
{
	if (!inTray) return;
	if (isClose)
		ShowWindow(SW_MINIMIZE);
	else
	{
		ShowWindow(SW_SHOWNORMAL); SetForegroundWindow();
	}
	NOTIFYICONDATA nid;
	nid.cbSize = (size_t)sizeof(NOTIFYICONDATA);
	nid.hWnd = this->m_hWnd;
	nid.uID = IDI_PUBSERVER;
	nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
	nid.uCallbackMessage = WM_SHOW_MAIN;
	nid.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_PUBSERVER));
	wcscpy_s(nid.szTip, PubServer_TITLE);
	Shell_NotifyIcon(NIM_DELETE, &nid);
	inTray = false;
}

void CPubServer::setCtrlShowTime(const CPlainTime &plain)
{
	cs_refresh.Lock();
	m_NowDisplay->setTime(plain.hour, plain.minute, plain.second, plain.ms);
	cs_refresh.Unlock();
}

void CPubServer::SetTimeClr(bool useSysTime, bool adjTimer)
{
	cs_refresh.Lock();
	if (useSysTime)
		m_NowDisplay->setTextColor(adjTimer ? RGB(255, 242, 0) : RGB(255, 255, 255));
	else
		m_NowDisplay->setTextColor(adjTimer ? RGB(255, 128, 64) : RGB(255, 0, 0));
	cs_refresh.Unlock();
}

void CPubServer::ShowData()
{
	CString v; CPubInfoData data, *pData = m_pNode && m_pNode->GetPubInfoData(data) ? &data : nullptr;
	cs_refresh.Lock();
	if (!pData || pData->getMinPrice() <= 0) v = L"N/A"; else v.Format(L"%d", pData->getMinPrice()); m_Min.SetWindowText(v);
	if (!pData || pData->getMaxPrice() <= 0) v = L"N/A"; else v.Format(L"%d", pData->getMaxPrice()); m_Max.SetWindowText(v);
	if (!pData || pData->getMinTm() < 0) m_MinTimeDisplay->setNA();
	else { cst_Time tm; tm.fromInt(pData->getMinTm()); m_MinTimeDisplay->setTime(tm.hour, tm.minute, tm.second); }
	ShowClientNum();
	cs_refresh.Unlock();
}

void CPubServer::ShowClientNum()
{
	if (m_clientNum < 0)
		m_CltNum.SetWindowText(L"N/A");
	else
	{
		intptr_t nowNum = m_pNode ? m_pNode->GetClientNum() : 0;
		CString v; v.Format(L"%d / %d", (int)nowNum, (int)m_clientNum); m_CltNum.SetWindowText(v);
	}
}

void CPubServer::DoSystime()
{
	if (!m_useSysTime)
	{
		m_useSysTime = true; m_Now.getLocalTime();
		m_NonAdjTimes = m_WrongAdjTimes = 0;
		if (m_adjTimer == NULL) m_adjTimer = SET_USER_TIMER(TimerPubServer + 3, 133);
		CALL_USER_TIMER(TimerPubServer + 2); SetTimeClr(m_useSysTime, m_adjTimer ? true : false);
	}
}

void CPubServer::DoBidtime(const cst_tickTime_Page &t)
{
	if (m_useSysTime)
	{
		m_useSysTime = false; m_Now.Init(t);
		m_NonAdjTimes = m_WrongAdjTimes = 0;
		if (m_adjTimer == NULL) m_adjTimer = SET_USER_TIMER(TimerPubServer + 3, 133);
		CALL_USER_TIMER(TimerPubServer + 2); SetTimeClr(m_useSysTime, m_adjTimer ? true : false);
	}
}

//返回 当前时间-公共信息时间 >0有延迟 <0根据doAdjust判断是否需要调整当前时间
const DFTick CPubServer::GetTimeDiff(const cst_tickTime_Page &t, bool doAdjust)
{
	DFTick df = m_Now.Raw(t.tick) - t.Raw();
	if (df < 0 && doAdjust)
	{
		m_NonAdjTimes = 0;
		m_Now.SetRaw(m_Now.Raw() - df);
	}
	return df;
}






//CIOCPSocketUIDelegate
CIOCPSocketUIDelegate::CIOCPSocketUIDelegate(CIOCPListenSocket *listensocket)
	:IIOCPSocketDelegate(listensocket), stopping(true)
{
	nowNum = maxNum = 0;
}

CIOCPSocketUIDelegate::~CIOCPSocketUIDelegate()
{
	StopService();
}

void CIOCPSocketUIDelegate::StartService(LPVOID param)
{
	cs_start.Lock();
	stopping = true;
	if (skListen) Stop_Service(param);
	intptr_t num = Start_Service(param);
	if (num > 0)
	{
		maxNum = num; stopping = false;
	}
	cs_start.Unlock();
}
void CIOCPSocketUIDelegate::StopService(LPVOID param)
{
	cs_start.Lock();
	if (skListen)
	{
		stopping = true;
		if (Stop_Service(param)) maxNum = 0;
	}
	cs_start.Unlock();
}

//返回是否停止
bool CIOCPSocketUIDelegate::Stop_Service(LPVOID param)
{
	skListen->Destroy(nullptr); delete skListen; skListen = nullptr;
	return true;
}

void CIOCPSocketUIDelegate::logError(CIOCPSocket * socket, intptr_t errorCode, IOCP_OPERATOR oper, intptr_t sysErrorCode)
{
	/*
	char errMsg[1024]; socket->GetErrorExplanation(errMsg, sizeof(errMsg), errorCode, oper, sysErrorCode);
	m_pMain->writeLog(errMsg);
	*/
}

void CIOCPSocketUIDelegate::logDebug(LPCSTR sFormat, ...)
{
	/*
	if (!m_pMain->mLog) return;
	va_list pArgList;
	va_start(pArgList, sFormat);
	m_pMain->createLog(WRLOG_MustFlush, sFormat, pArgList);
	va_end(pArgList);
	*/
}





//CPubInfoNode
CPubInfoNode::CPubInfoNode(TICK nowTk, CPubInfoClient *sender, const stPubInfoPacket *pPacket)
	:cltAry(CPubInfoClientArray::CLEAR_METHOD::None)
{
	InitializeSRWLock(&cs_infoLock);
	lastMsgTick = nowTk;
	pSender = sender; cltAry.add(sender);
	packet.headMark = PUBINFO_HEADMARK;
	packet.infoMark = pPacket->infoMark;
	packet.data = pPacket->data;
	packet.crc = pPacket->crc;
	cst_Time nowTM; nowTM.fromInt(packet.data.getNowTm());
	m_HookTime.Init(lastMsgTick, nowTM);
}

bool CPubInfoNode::SetPacket(CPubInfoClient *sender, const stPubInfoPacket *pPacket, bool newPacket)
{
	AcquireSRWLockExclusive(&cs_infoLock);
	if (newPacket) cltAry.add(sender);
	bool isNew = !pSender || packet.data.isEmpty() || pPacket->data > packet.data;
	if (isNew)
	{
		pSender = sender;
		packet.crc = pPacket->crc;
		packet.data = pPacket->data;
		cst_Time nowTM; nowTM.fromInt(packet.data.getNowTm());
		if (m_HookTime.tick.IsEmpty() || nowTM > m_HookTime.t || (nowTM == m_HookTime.t && lastMsgTick < m_HookTime.tick))
			m_HookTime.Init(lastMsgTick, nowTM);
	}
	ReleaseSRWLockExclusive(&cs_infoLock);
	return isNew;
}

void CPubInfoNode::RemoveClient(CPubInfoClient *pPctClt)
{
	AcquireSRWLockExclusive(&cs_infoLock);
	cltAry.del(pPctClt);
	ReleaseSRWLockExclusive(&cs_infoLock);
}

void CPubInfoNode::CheckSend()
{
	if (TryAcquireSRWLockShared(&cs_infoLock))
	{
		if (packet.infoMark)
		{
			for (intptr_t i = 0; i < cltAry.size(); i++) if (cltAry[i] && cltAry[i]->socket && cltAry[i] != pSender)
				cltAry[i]->SendPubinfo(&packet);
		}
		ReleaseSRWLockShared(&cs_infoLock);
	}
}

bool CPubInfoNode::GetPubInfoTime(cst_tickTime_Page &t)
{
	AcquireSRWLockShared(&cs_infoLock);
	bool ava = m_HookTime.tick.IsAvailable();
	if (ava) t = m_HookTime;
	ReleaseSRWLockShared(&cs_infoLock);
	return ava;
}

bool CPubInfoNode::GetPubInfoData(CPubInfoData &data)
{
	AcquireSRWLockShared(&cs_infoLock);
	bool ava = !!packet.infoMark;
	if (ava) data = packet.data;
	ReleaseSRWLockShared(&cs_infoLock);
	return ava;
}

intptr_t CPubInfoNode::GetClientNum()
{
	AcquireSRWLockShared(&cs_infoLock);
	intptr_t num = cltAry.size();
	ReleaseSRWLockShared(&cs_infoLock);
	return num;
}




//CPubInfoNodeArray
CPubInfoNodeArray::CPubInfoNodeArray(IUserDefineDelete<CPubInfoNode*> *deleter)
	: CPointerArray<CPubInfoNode*>(deleter ? CPointerArray::CLEAR_METHOD::UserDefine : CPointerArray::CLEAR_METHOD::Delete)
{
	if (deleter) SetUserDefine(deleter);
	InitializeSRWLock(&cs_lock);
}

void CPubInfoNodeArray::clear()
{
	AcquireSRWLockExclusive(&cs_lock);
	CPointerArray::clear();
	ReleaseSRWLockExclusive(&cs_lock);
}

bool CPubInfoNodeArray::CheckTimeout()
{
	bool deleted = false;
	if (TryAcquireSRWLockExclusive(&cs_lock))
	{
		for (intptr_t i = 0; i < count;)
			if (recs[i] && recs[i]->lastMsgTick.IsAvailable() && TICK::Now() - recs[i]->lastMsgTick > 1800000)	//30分钟没有收到公共信息，清空数据
			{
				del(i); deleted = true;
			}
			else
				i++;
		ReleaseSRWLockExclusive(&cs_lock);
	}
	return deleted;
}

const CPubInfoNode * CPubInfoNodeArray::SetPacket(TICK nowTk, CPubInfoClient *sender, const stPubInfoPacket *pPacket, 
	bool newPacket, bool &nodeAdded, bool &needSend)
{
	CPubInfoNode *pNode = nullptr; nodeAdded = false;
	AcquireSRWLockShared(&cs_lock);
	for (intptr_t i = 0; i < count; i++) if (recs[i] && recs[i]->getInfoMark() == pPacket->infoMark)
	{
		pNode = recs[i]; pNode->lastMsgTick = nowTk; needSend = pNode->SetPacket(sender, pPacket, newPacket);
		break;
	}
	ReleaseSRWLockShared(&cs_lock);
	if (pNode) return pNode;
	if ((pNode = new CPubInfoNode(nowTk, sender, pPacket)) != nullptr)
	{
		AcquireSRWLockExclusive(&cs_lock);
		if (!add(pNode)) { delete pNode; pNode = nullptr; }
		ReleaseSRWLockExclusive(&cs_lock);
	}
	if (pNode) nodeAdded = true;
	needSend = false;	//第一个Client不需要发
	return pNode;
}

void CPubInfoNodeArray::RemoveClient(CPubInfoClient *pPctClt)
{
	AcquireSRWLockShared(&cs_lock);
	for (intptr_t i = 0; i < count; i++) if (recs[i]) recs[i]->RemoveClient(pPctClt);
	ReleaseSRWLockShared(&cs_lock);
}

void CPubInfoNodeArray::CheckSend(CPubInfoNode * pNode)
{
	AcquireSRWLockShared(&cs_lock);
	if (recs) for (intptr_t i = 0; i < count; i++) if (recs[i] == pNode)
	{
		pNode->CheckSend(); break;
	}
	ReleaseSRWLockShared(&cs_lock);
}

//返回pDefaul是否改变
bool CPubInfoNodeArray::SetCombo(CComboBox &combo, const CPubInfoNode *pDefault)
{
	combo.ResetContent(); SYSTEMTIME tm; GetLocalTime(&tm); int defIndex = -1;
	AcquireSRWLockShared(&cs_lock);
	for (intptr_t i = 0; i < count; i++) if (recs[i])
	{
		unsigned short infomark = recs[i]->getInfoMark(); if (!infomark) continue;
		CString v;
		switch (infomark & 0xf)
		{
		case 0xe:	//常规模拟0xDD0e
			if (infomark != ((tm.wDay << 8) | 0xe)) continue;
			v = L"常规模拟(4分钟或全时段)"; break;
		case 0xd:	//正式拍牌0xDD0d
			if (infomark != ((tm.wDay << 8) | 0xd)) continue;
			v.Format(L"正式拍卖会(%d年%d月)", (int)tm.wYear, (int)tm.wMonth); break;
		default:	//全真模拟0xYYMM
			for (intptr_t k = 0; k < auctionAry.count; k++)
			{
				LPCWSTR auc = auctionAry.str[k]; if (!auc) continue;
				if (infomark != ((((auc[2] & 0xf) * 10 + (auc[3] & 0xf)) << 8) | ((auc[4] & 0xf) * 10 + (auc[5] & 0xf)))) continue;
				v.Format(L"[%s] 全真模拟", auc); break;
			}
			if (v.IsEmpty()) continue;
		}
		int j = combo.AddString(v);
		if (j >= 0)
		{
			combo.SetItemData(j, (DWORD_PTR)recs[i]);
			if (pDefault == recs[i]) defIndex = j;
		}
	}
	ReleaseSRWLockShared(&cs_lock);
	bool defChanged = false;
	if (!pDefault)
	{
		if (combo.GetCount() > 0) { defIndex = 0; defChanged = true; }
	}
	else if (defIndex < 0)
	{
		if (combo.GetCount() > 0) defIndex = 0;
		defChanged = true;
	}
	combo.SetCurSel(defIndex);
	return defChanged;
}





//CPubServerDelegate
CPubServerDelegate::CPubServerDelegate(CPubServer *owner)
	:CIOCPSocketUIDelegate(nullptr), m_Owner(owner), m_pubInfoProcesser(this), nodeAry(this), nodeBin(nullptr)
{
	hBroadcastThread = nullptr;	broadcastSock = INVALID_SOCKET;
}

CPubServerDelegate::~CPubServerDelegate()
{
}

DWORD WINAPI StartThread_Broadcast(LPVOID p)
{
	CPubServerDelegate *pParam = (CPubServerDelegate *)p; if (!pParam) return -1;
	pParam->broadcastThread();
	pParam->threadRunning = false;
	return 0;
}
intptr_t CPubServerDelegate::Start_Service(LPVOID param)
{
	stDelegateStartParam *p = (stDelegateStartParam*)param; if (!p) return 0;
	workNum = 0; intptr_t num = p->maxNum;
	skListen = new CIOCPListenSocket(this, 10, num); if (!skListen) return 0;
	m_pClt = (CPubInfoClient*)HeapAlloc(GetProcessHeap(), 0, num * sizeof(CPubInfoClient));
	if (m_pClt)	//由于涉及继承，需要初始化一个记录并复制
	{
		CPubInfoClient sample;
		for (intptr_t i = 0; i < num; i++) memcpy_s(m_pClt + i, sizeof(CPubInfoClient), &sample, sizeof(CPubInfoClient));
	}
	if (!m_pClt || !skListen->Create(PubInfo_Port, nullptr) || !skListen->Accept())
	{
		Stop_Service(param); return 0;
	}
	m_pCltEnd = m_pClt + num;
	if (p->processer) skListen->setHostProcesser(p->processer);
	m_pubInfoProcesser.SetCRCAdd();	nodeAry.clear(); nodeBin.clear();
	clientNum = 0;
	threadRunning = true; hBroadcastThread = CreateThread(NULL, 0, StartThread_Broadcast, this, 0, NULL);
	PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_CLTNUM, (WPARAM)clientNum);
	return num;
}

bool CPubServerDelegate::Stop_Service(LPVOID param)
{
	setBroadcastSocket(INVALID_SOCKET);
	if (hBroadcastThread)
	{
		stopThread = true; while (CheckThread(hBroadcastThread, threadRunning)) Sleep(10);
	}
	while (workNum) SleepEx(10, TRUE);
	bool bRet = CIOCPSocketUIDelegate::Stop_Service(param);
	if (bRet && m_pClt)
	{
		CPubInfoClient * pPctClt = m_pClt;
		for (intptr_t i = 0; i < maxNum; i++, pPctClt++) pPctClt->free();
		HeapFree(GetProcessHeap(), 0, (LPVOID)m_pClt);
		m_pClt = m_pCltEnd = nullptr;
		nodeAry.clear(); nodeBin.clear();
		PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_CLTNUM, (WPARAM)-1);
	}
	return bRet;
}

//每分钟检查一次是否有超时客户
void CPubServerDelegate::CheckTimeout()
{
	InterlockedIncrement(&workNum);
	if (IsRunning())
	{
		CPubInfoClient * pPctClt = m_pClt; for (intptr_t i = 0; i < maxNum; i++, pPctClt++)
			if (pPctClt->socket && TryAcquireSRWLockShared(&pPctClt->cs_client))
			{
				bool tmout = (TICK::Now() - pPctClt->lastTick >= 300000);	//5分钟无操作即超时
				ReleaseSRWLockShared(&pPctClt->cs_client);
				if (tmout) pPctClt->socket->DisconnectEx();
			}
		if (nodeAry.CheckTimeout())	PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_NODES);
	}
	InterlockedDecrement(&workNum);
}

CPubInfoClient * CPubServerDelegate::GetClient(CAcceptSocket *socket, bool readOnly)
{
	if (socket)
	{
		CPubInfoClient *pPctClt = (CPubInfoClient *)socket->Ptr();
		if (pPctClt >= m_pClt && pPctClt < m_pCltEnd && pPctClt->socket == socket)
		{
			if (readOnly)
				AcquireSRWLockShared(&pPctClt->cs_client);
			else
				AcquireSRWLockExclusive(&pPctClt->cs_client);
			if (pPctClt->socket == socket) return pPctClt;
			if (readOnly)
				ReleaseSRWLockShared(&pPctClt->cs_client);
			else
				ReleaseSRWLockExclusive(&pPctClt->cs_client);
		}
	}
	else
	{
		CPubInfoClient *pPctClt = m_pClt;
		if (pPctClt) for (intptr_t i = 0; i < maxNum; i++, pPctClt++)
		{
			if (!pPctClt->socket)
			{
				if (readOnly)
					AcquireSRWLockShared(&pPctClt->cs_client);
				else
					AcquireSRWLockExclusive(&pPctClt->cs_client);
				if (!pPctClt->socket) return pPctClt;
				if (readOnly)
					ReleaseSRWLockShared(&pPctClt->cs_client);
				else
					ReleaseSRWLockExclusive(&pPctClt->cs_client);
			}
		}
	}
	return nullptr;
}

void CPubServerDelegate::ClientConnected(CAcceptSocket *socket)
{
	CIOCPSocketUIDelegate::ClientConnected(socket);
	InterlockedIncrement(&workNum);
	if (IsRunning())
		do {
			CPubInfoClient * pPctClt =  GetClient((CAcceptSocket*)nullptr, false); if (!pPctClt) break;
			if (!pPctClt->socket)
			{
				pPctClt->socket = socket; socket->SetPtr(pPctClt);
			}
			pPctClt->lastTick = TICK::Now();
			//m_pMain->writeLog("公共信息端%s登录", pPctClt->ip);
			ReleaseSRWLockExclusive(&pPctClt->cs_client);
			InterlockedIncrement(&clientNum);
			//PostRefreshMsg(Rfmsg_PubInfo_Connect, (LPARAM)pPctClt);
			PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_CLTNUM, (WPARAM)clientNum);
		} while (0);
	InterlockedDecrement(&workNum);
}

void CPubServerDelegate::ClientDisconnected(CAcceptSocket *socket)
{
	CIOCPSocketUIDelegate::ClientDisconnected(socket);
	InterlockedIncrement(&workNum);
	do {
		CPubInfoClient * pPctClt = GetClient(socket, false); if (!pPctClt) break;
		//m_pMain->writeLog("公共信息端%s退出", pPctClt->ip);
		nodeAry.RemoveClient(pPctClt);
		pPctClt->free(); pPctClt->socket = nullptr;
		ReleaseSRWLockExclusive(&pPctClt->cs_client);
		InterlockedDecrement(&clientNum);
		//PostRefreshMsg(Rfmsg_PubInfo_Disconnect, (LPARAM)pPctClt);
		PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_CLTNUM, (WPARAM)clientNum);
	} while (0);
	if (socket) socket->SetPtr(nullptr);
	InterlockedDecrement(&workNum);
}

void CPubServerDelegate::DataRecved(CAcceptSocket *socket, const byte * data, intptr_t dataLength)
{
	bool needRecv = true;
	socket->CombineRecv(data, dataLength, &needRecv);
	if (needRecv)
	{
		if (!socket->AcceptRecv()) socket->DisconnectEx();
	}
}

void CPubServerDelegate::DataSended(CAcceptSocket *socket, LPVOID context)
{
	if (context != DONT_RECV_AFTER_SEND)
	{
		if (!socket->AcceptRecv()) socket->DisconnectEx();
	}
}

void CPubServerDelegate::ProcPubInfo(CAcceptSocket *socket, const stPubInfoPacket *pPacket
#ifdef USE_OLD_PUBINFO_PACKET
	, const stPubInfoPacket_Old *pOldPacket
#endif
)
{
	InterlockedIncrement(&workNum);
	if (IsRunning())
		do {
			TICK tkNow = TICK::Now();
			CPubInfoClient * pPctClt = GetClient(socket, false); if (!pPctClt) break;
			pPctClt->SetLastTick();
			intptr_t iPacket = pPctClt->CheckPubinfo(pPacket
#ifdef USE_OLD_PUBINFO_PACKET
				, pOldPacket
#endif
			);
			ReleaseSRWLockExclusive(&pPctClt->cs_client);
			if (iPacket)
			{
				bool nodeAdded, needSend; const CPubInfoNode *pNode = nodeAry.SetPacket(tkNow, pPctClt, pPacket, iPacket < 0, nodeAdded, needSend);
				if (needSend) SendMsg(m_Owner->GetSafeHwnd(), WM_CHECK_SEND, (WPARAM)pNode);
				if (nodeAdded)
					PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_NODES);
				else if (pNode)
					PostMsg(m_Owner->GetSafeHwnd(), WM_REFRESH_NODES, (WPARAM)pNode);
			}
		} while (0);
	InterlockedDecrement(&workNum);
}

void CPubServerDelegate::userDefineDelete(CPubInfoNode* rec)
{
	nodeBin.add(rec);
}

BPS_Packet *CPubServerDelegate::getMyPacket()	//获取自己的ip地址
{
	BPS_Packet *pPacket = nullptr;
	do {
		char host_name[256]; if (gethostname(host_name, sizeof(host_name))) break;
		struct hostent FAR * p = gethostbyname(host_name); if (!p) break;
		unsigned int ipnum = 0; for (intptr_t i = 0; p->h_addr_list[i]; i++, ipnum++); if (!ipnum) break;
		pPacket = (BPS_Packet *)HeapAlloc(GetProcessHeap(), 0, sizeof(BPS_Packet) + ipnum * sizeof(in_addr)); if (!pPacket) break;
		pPacket->ipNum = ipnum;
		for (intptr_t i = 0; p->h_addr_list[i]; i++)
			pPacket->myIP[i].s_addr = ((struct in_addr*)p->h_addr_list[i])->s_addr;
		pPacket->headMark = BPS_HEADMARK;
		pPacket->packetSize = sizeof(BPS_Packet) + pPacket->ipNum * sizeof(in_addr);
		pPacket->crc = pPacket->getCRC();
	} while (0);
	return pPacket;
}

void CPubServerDelegate::broadcastThread()
{
	BPS_Packet *pMyPacket = CPubServerDelegate::getMyPacket();	//获取自己的ip地址
	if (!pMyPacket) return;
	for (stopThread = false; !stopThread; )
	{
		SOCKET sock = INVALID_SOCKET;
		do {
			sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) break;
			BOOL bTrue = TRUE; setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (LPCSTR)&bTrue, sizeof(BOOL));
			setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (LPCSTR)&bTrue, sizeof(BOOL));
			sockaddr_in saServer; ZeroMemory(&saServer, sizeof(saServer));
			saServer.sin_family = AF_INET;
			saServer.sin_port = htons(PubInfoBroadcast_Port);
			saServer.sin_addr.s_addr = htonl(INADDR_ANY);
			if (bind(sock, (sockaddr*)&saServer, sizeof(saServer))) break;
			setBroadcastSocket(sock); sock = INVALID_SOCKET;
			while (!stopThread && broadcastSock != INVALID_SOCKET)
			{
				int sockaddr_Size = sizeof(saServer); char data[1024];
				int dataLength = recvfrom(broadcastSock, data, sizeof(data), 0, (sockaddr*)&saServer, &sockaddr_Size);
				if (dataLength <= 0 || broadcastSock == INVALID_SOCKET) break;
				const BPS_Packet *pRecv = (const BPS_Packet*)data;
				if (dataLength < sizeof(BPS_Packet) || pRecv->headMark != BPS_HEADMARK ||
					pRecv->packetSize != dataLength || pRecv->ipNum <= 0 ||
					pRecv->packetSize != sizeof(BPS_Packet) + pRecv->ipNum * sizeof(in_addr) ||
					pRecv->crc != pRecv->getCRC()) continue;
				char sendData[1024]; BPS_Packet *pSend = (BPS_Packet*)sendData;
				pSend->headMark = BPS_HEADMARK; pSend->ipNum = 0;
				for (intptr_t i = 0; i < pMyPacket->ipNum; i++) for (intptr_t j = 0; j < pRecv->ipNum; j++)
					if (pMyPacket->myIP[i].S_un.S_un_w.s_w1 == pRecv->myIP[j].S_un.S_un_w.s_w1)	//保留ip地址前2字节相同
					{
						pSend->myIP[pSend->ipNum].s_addr = pMyPacket->myIP[i].s_addr; pSend->ipNum++; break;
					}
				pSend->packetSize = sizeof(BPS_Packet) + pSend->ipNum * sizeof(in_addr);
				pSend->crc = pSend->getCRC();
				sendto(broadcastSock, sendData, pSend->packetSize, 0, (sockaddr*)&saServer, sockaddr_Size);
			}
			setBroadcastSocket(INVALID_SOCKET);
		} while (false);
		if (sock != INVALID_SOCKET) { shutdown(sock, SD_BOTH); closesocket(sock); }
		for (TICK tk = TICK::Now(5000); !stopThread&&TICK::Now() < tk; Sleep(10));
	}
	HeapFree(GetProcessHeap(), 0, pMyPacket);
}

void CPubServerDelegate::setBroadcastSocket(SOCKET sock)
{
	sock = (SOCKET)InterlockedExchange(&broadcastSock, sock);
	if (sock != INVALID_SOCKET)
	{
		shutdown(sock, SD_BOTH); closesocket(sock);
	}
}





//CPubInfoClient
CPubInfoClient::CPubInfoClient()
{
	InitializeSRWLock(&cs_client);
	socket = nullptr; 
	pubinfoPacket.headMark = PUBINFO_HEADMARK;
	pubinfoPacket.infoMark = 0;
	pubinfoPacket.data.clear();
	crcErrorTime = 0;
#ifdef USE_OLD_PUBINFO_PACKET
	oldPacket.headMark = PUBINFO_HEADMARK_OLD;
	oldPacket.infoMark = 0;
#endif
}
void CPubInfoClient::free()
{
	pubinfoPacket.headMark = PUBINFO_HEADMARK;
	pubinfoPacket.infoMark = 0;
	pubinfoPacket.data.clear();
	crcErrorTime = 0;
#ifdef USE_OLD_PUBINFO_PACKET
	oldPacket.headMark = PUBINFO_HEADMARK_OLD;
	oldPacket.infoMark = 0;
#endif
}

//返回0=错误或不需要更新的包 -1=第一个包 1=需要更新的包
intptr_t CPubInfoClient::CheckPubinfo(const stPubInfoPacket *pPacket
#ifdef USE_OLD_PUBINFO_PACKET
	, const stPubInfoPacket_Old *pOldPacket
#endif
)
{
	//错误超过3次则不再处理此连接
	if (crcErrorTime >= 3) return 0;
	if (!pPacket)	//crc错误
	{
		crcErrorTime++;
		//if (crcErrorTime >= 3) m_pMain->writeDBLog("CRC错误超过3次 IP=%s", ip);
		return 0;
	}
	if (pPacket->data.isEmpty()) return 0;
	intptr_t result;
	if (!pubinfoPacket.infoMark)
	{
		pubinfoPacket.infoMark = pPacket->infoMark;
		result = -1;
	}
	else
	{
		if (pPacket->infoMark != pubinfoPacket.infoMark) return 0;
		if (!pubinfoPacket.data.isEmpty() && !(pPacket->data > pubinfoPacket.data)) return 0;
		result = 1;
	}
#ifdef USE_OLD_PUBINFO_PACKET
	if (pOldPacket) oldPacket.infoMark = pOldPacket->infoMark;
#endif
	pubinfoPacket.data = pPacket->data;
	return result;
}

void CPubInfoClient::SendPubinfo(const stPubInfoPacket *pPacket)
{
	if (!TryAcquireSRWLockExclusive(&cs_client)) return;
	if (socket && pPacket->infoMark == pubinfoPacket.infoMark &&
		(pubinfoPacket.data.isEmpty() || pPacket->data > pubinfoPacket.data))
	{
		pubinfoPacket.data = pPacket->data;
#ifdef USE_OLD_PUBINFO_PACKET
		if (oldPacket.infoMark)
		{
			oldPacket.data = pPacket->data;
			if (!socket->AcceptSend((byte*)&oldPacket, sizeof(stPubInfoPacket_Old), DONT_RECV_AFTER_SEND))
				socket->DisconnectEx();
		}
		else
#endif
		{
			if (!socket->AcceptSend((byte*)pPacket, sizeof(stPubInfoPacket), DONT_RECV_AFTER_SEND))
				socket->DisconnectEx();
		}
	}
	ReleaseSRWLockExclusive(&cs_client);
}

bool CPubInfoClient::ClearPubinfo(const stPubInfoPacket *pPacket)
{
	AcquireSRWLockExclusive(&cs_client); bool bRet = false;
	if (socket && pubinfoPacket.infoMark == pPacket->infoMark)
	{
		pubinfoPacket.data.clear();
		if (pPacket)
		{
			//SetLastCMD(LASTCMD::ServerRequest, "清空公共信息");
			if (socket->AcceptSend((byte*)pPacket, sizeof(stPubInfoPacket), DONT_RECV_AFTER_SEND))
				bRet = true;
			else
				socket->DisconnectEx();
		}
	}
	ReleaseSRWLockExclusive(&cs_client); return bRet;
}

#endif


